package main

import (
	"fmt"
	"github.com/go-xorm/xorm"
	"github.com/kataras/iris/core/errors"
	"github.com/ngaut/log"
	"github.com/shopspring/decimal"
	"global/structs"
	"server/ote/model"
	"time"
)

type HandlerDB struct {
	start 		time.Time
	coinPair 	structs.CoinPair
}

func NewHandlerDB(coinPair structs.CoinPair) *HandlerDB {
	return &HandlerDB{
		start: 		time.Now(),
		coinPair: 	coinPair,
	}
}

// 将委托单状态修改为老订单
func (h *HandlerDB) SetTrustCoinIsNewToOld(tid int) (bool, error) {
	return model.TrustCoinModel.UpdateIsNew(tid, model.IsNew_No)
}

// 保存到OTE_LOG表
func (h *HandlerDB) SaveOteLog(oteLog *structs.OteLog) (bool, error) {
	return model.OteLogModel.Insert(oteLog)
}

// 撤销委托
func (h *HandlerDB) Cancel(trust structs.TrustCoin) bool {

	var err error
	var dbOptSuccess, ignoreSaveToDB = false, false

	defer func() {
		if !ignoreSaveToDB {
			oteLog := NewOteLogWithoutResults(trust, true, dbOptSuccess)
			if oteLog == nil {
				return
			}
			if err != nil {
				oteLog.ErrorCode = 3
				oteLog.ErrorMsg = err.Error()
			}
			if _, err := model.OteLogModel.Insert(oteLog); err != nil {
				h.logError("insert ote log failed, msg: %s", err.Error())
			}
		}
	}()

	sess := model.MainDB.NewSession()
	defer sess.Close()

	err = sess.Begin()
	if err != nil {
		h.logError("session begin error, msg: %s", err.Error())
		return false
	}

	item, err := model.TrustCoinModel.Get(trust.Id)
	if err != nil {
		err = fmt.Errorf("get trust coin item error, msg: %s", err.Error())
		h.logError(err.Error())
		return false
	}

	if item.Id == 0 || item.Uid == 0 {
		err = fmt.Errorf("get trust coin return nil, tid: %d", trust.Id)
		h.logError(err.Error())
		return false
	}

	// 已撤销或者已全部成交不可修改状态
	if item.Status == int(model.Status_Revoke) || item.Status == int(model.Status_All) {
		err = fmt.Errorf("trust status is not right, tid: %d, status: %v", trust.Id, item.Status)
		h.logInfo(err.Error())
		ignoreSaveToDB = true
		return false
	}

	// 解冻资产
	if structs.TaskType(item.Flag) == structs.TASK_BUY {
		_, err = h.thawBuyerCoin(sess, item)
		if err != nil {
			h.logError("thaw buyer coin error, msg: %s", err.Error())
			sess.Rollback()
			return false
		}
	} else {
		_, err = h.thawSellerCoin(sess, item)
		if err != nil {
			h.logError("thaw seller coin error, msg: %s", err.Error())
			sess.Rollback()
			return false
		}
	}

	if _, err = model.TrustCoinModel.Cancel(sess, trust.Id); err != nil {
		h.logError("change trust status error, msg: %s", err.Error())
		sess.Rollback()
		return false
	}

	if err = sess.Commit(); err != nil {
		return false
	}

	dbOptSuccess = true

	// saveTrustCoinToHistory
	trust.Status = model.Status_Revoke
	conn := ote.GetOteRedis().Get()
	defer conn.Close()

	conn.Send("MULTI")
	saveTrustToHistoryDone(conn, h.coinPair.Name, trust.Uid, &trust)
	conn.Do("EXEC")

	h.logInfo("trust coin cancel success, tid: %d, takeTime: %s", trust.Id, time.Since(h.start).String())

	return true
}

// 撤销卖家委托单，解除冻结资本
func (h *HandlerDB) thawSellerCoin(sess *xorm.Session, coin *structs.TrustCoin) (bool, error) {
	userAccount, err := model.UserCoinModel.GetItemWithLock(sess, coin.Uid, coin.CoinFrom)
	if err != nil {
		return false, fmt.Errorf("get user account failed, tid: %d, uid: %d, err: %s", coin.Id, coin.Uid, err.Error())
	}

	numberAvailable, _ := decimal.NewFromString(coin.NumberAvailable)
	if numberAvailable.LessThanOrEqual(decimal.Zero) {
		return false, fmt.Errorf("trust number available is zero, tid: %d, available: %s", coin.Id, numberAvailable.StringFixedBank(8))
	}

	oldAccountLock, _ := decimal.NewFromString(userAccount.BalanceLock)
	if oldAccountLock.LessThan(numberAvailable) {
		return false, fmt.Errorf("user account less than trust number available, tid: %d, lock: %s, number: %s",
			coin.Id, oldAccountLock.StringFixedBank(16), numberAvailable.StringFixedBank(16))
	}

	userBalanceAvailable, _ := decimal.NewFromString(userAccount.BalanceAvailable)
	available := userBalanceAvailable.Add(numberAvailable)
	lock := oldAccountLock.Sub(numberAvailable)
	balance := available.Add(lock)
	return model.UserCoinModel.Update(
		sess,
		userAccount.Id,
		coin.CoinFrom,
		balance.StringFixedBank(16),
		available.StringFixedBank(16),
		lock.StringFixedBank(16))
}

// 买家解冻资产
func (h *HandlerDB) thawBuyerCoin(sess *xorm.Session, coin *structs.TrustCoin) (bool, error) {

	userAccount, err := model.UserCoinModel.GetItemWithLock(sess, coin.Uid, coin.CoinTo)
	if err != nil {
		return false, fmt.Errorf("get user coin failed, uid: %d, coin: %s, err: %s", coin.Uid, coin.CoinTo, err.Error())
	}

	available, _ := decimal.NewFromString(coin.NumberAvailable)
	price, _ := decimal.NewFromString(coin.Price)

	if available.LessThanOrEqual(decimal.Zero) {
		return false, fmt.Errorf("trust number available is none, tid: %d, available: %v", coin.Id, available.StringFixedBank(10))
	}

	total := available.Mul(price)
	userLock, _ := decimal.NewFromString(userAccount.BalanceLock)
	if userLock.LessThan(total) {
		return false, fmt.Errorf("user balance_lock less than total witch should return, tid: %d, total: %v, lock: %s",
			coin.Id, total.StringFixedBank(16), userLock.StringFixedBank(16))
	}

	oldAvailable, _ := decimal.NewFromString(userAccount.BalanceAvailable)

	userAvailable := oldAvailable.Add(total)
	userLock = userLock.Sub(total)
	balance := userAvailable.Add(userLock)

	return model.UserCoinModel.Update(
		sess,
		userAccount.Id,
		coin.CoinTo,
		balance.StringFixedBank(16),
		userAvailable.StringFixedBank(16),
		userLock.StringFixedBank(16))
}

// 异步保存到数据库
func (h *HandlerDB) Save(results *structs.Results) bool {
	var err error
	defer func() {
		oteLog := NewOteLog(results, structs.TaskType(results.ActiveTrust.TrustCoin.Flag))
		if err != nil {
			oteLog.ErrorCode = model.OTE_LOG_ERRCODE_DB
			oteLog.ErrorMsg = err.Error()
		}

		if _, err := h.SaveOteLog(oteLog); err != nil {
			h.logError("save ote log error, msg: %s", err.Error())
			return
		}

		h.logInfo("save result to ote log table success")
		h.logInfo("+++++++++++++++++++++++++++++++++++++++++++++++++++++")
	}()

	h.logInfo("")
	h.logInfo("*****************************************************")

	sess := model.MainDB.NewSession()
	defer sess.Close()

	err = sess.Begin()
	if err != nil {
		h.logError("session begin error, msg: %s", err.Error())
		return false
	}

	if _, err = h.updateTrustCoins(sess, results); err != nil {
		h.logError("update trust coin failed, takeTime: %s, msg: %s", time.Since(h.start).String(), err.Error())
		sess.Rollback()
		return false
	}

	if _, err = h.updateUserAccount(sess, results); err != nil {
		h.logError("update user account failed, takeTime: %s, msg: %s", time.Since(h.start).String(), err.Error())
		sess.Rollback()
		return false
	}

	if _, err = h.insertOrderAndFinance(sess, results.OrderLogs); err != nil {
		h.logError("insert order and finance failed, takeTime: %s, msg: %s", time.Since(h.start).String(), err.Error())
		sess.Rollback()
		return false
	}

	// get asset log
	if _, err := h.getAssetLog(results); err != nil {
		h.logError("get asset log error, takeTime: %s, msg: %s", time.Since(h.start).String(), err.Error())
		sess.Rollback()
		return false
	}

	// user asset log
	if _, err = h.insertUserAssetLog(sess, results.UserAssetLogs); err != nil {
		h.logError("insert user asset log failed, takeTime: %s, msg: %s", time.Since(h.start).String(), err.Error())
		sess.Rollback()
		return false
	}

	if err = sess.Commit(); err != nil {
		h.logError("commit error, tid: %d, take time: %s, msg: %s", results.ActiveTrust.TrustId, time.Since(h.start).String(), err.Error())
		return false
	}


	// save order to redis

	h.logInfo("commit success, takeTime: %s", time.Since(h.start).String())

	results.DbOptSuccess = true
	return true
}

// 组合交易流水日志
func (h *HandlerDB) getAssetLog(results *structs.Results) (bool, error) {
	for _, trust := range results.OrderLogs {
		buyAccount, buyAccountOK := results.UserCoin[trust.BuyUid]
		saleAccount, saleAccountOK := results.UserCoin[trust.SaleUid]
		if !buyAccountOK || !saleAccountOK {
			return false, fmt.Errorf("user account info not found in userAccount, buy uid: %d, sale uid: %d", trust.BuyUid, trust.SaleUid)
		}

		buyUserCoinFrom, bfOK 		:= buyAccount.NewUserCoin[h.coinPair.CoinFrom]
		buyUserCoinTo, btOK 		:= buyAccount.NewUserCoin[h.coinPair.CoinTo]

		saleUserCoinFrom, sfOK 		:= saleAccount.NewUserCoin[h.coinPair.CoinFrom]
		saleUserCoinTo, stOK 		:= saleAccount.NewUserCoin[h.coinPair.CoinTo]

		if !bfOK || !btOK || !sfOK || !stOK {
			return false, errors.New("can not read user account info")
		}

		// 卖家 coin from
		saleUserFromBalanceAvailable, _ := decimal.NewFromString(saleUserCoinFrom.BalanceAvailable)
		saleUserFromBalanceLock, _ := decimal.NewFromString(saleUserCoinFrom.BalanceLock)

		saleFromLock := saleUserFromBalanceLock.Add(trust.SellerFromLock)

		saleUserCoinFrom.Balance = saleUserFromBalanceAvailable.Add(saleFromLock).StringFixedBank(16)
		saleUserCoinFrom.BalanceLock = saleFromLock.StringFixedBank(16)

		results.UserAssetLogs = append(results.UserAssetLogs, h.userAssetLog(trust.SaleUid,
			trust.Order.Id,
			h.coinPair.CoinFrom,
			trust.DealNumber.StringFixedBank(16),
			decimal.Zero.StringFixedBank(16),
			saleUserCoinFrom.Balance,
			saleUserCoinFrom.BalanceAvailable,
			saleUserCoinFrom.BalanceLock,
			model.ASSET_TYPE_SALEOUT))

		// buy user from type
		// 买家 扣 coin_from，数量 * 税率
		buyUserFromBalanceAvailable, _ := decimal.NewFromString(buyUserCoinFrom.BalanceAvailable)
		buyUserFromBalanceLock, _ := decimal.NewFromString(buyUserCoinFrom.BalanceLock)

		buyFromAvailable := buyUserFromBalanceAvailable.Add(trust.BuyerFromOver).Sub(trust.BuyerFee)

		buyUserCoinFrom.Balance = buyFromAvailable.Add(buyUserFromBalanceLock).StringFixedBank(16)
		buyUserCoinFrom.BalanceAvailable = buyFromAvailable.StringFixedBank(16)

		results.UserAssetLogs = append(results.UserAssetLogs, h.userAssetLog(trust.BuyUid,
			trust.Order.Id,
			h.coinPair.CoinFrom,
			trust.DealNumber.StringFixedBank(16),
			trust.BuyerFee.StringFixedBank(16),
			buyUserCoinFrom.Balance,
			buyUserCoinFrom.BalanceAvailable,
			buyUserCoinFrom.BalanceLock,
			model.ASSET_TYPE_BUYIN))

		// 买家 coin_to，
		buyUserToAvailable, _ := decimal.NewFromString(buyUserCoinTo.BalanceAvailable)
		buyUserToLock, _ := decimal.NewFromString(buyUserCoinTo.BalanceLock)

		buyToLock := buyUserToLock.Add(trust.BuyerToLock)
		buyToAvailable := buyUserToAvailable.Add(trust.BuyerToOver)

		buyUserCoinTo.BalanceLock = buyToLock.StringFixedBank(16)
		buyUserCoinTo.BalanceAvailable = buyToAvailable.StringFixedBank(16)
		buyUserCoinTo.Balance = buyToLock.Add(buyToAvailable).StringFixedBank(16)

		results.UserAssetLogs = append(results.UserAssetLogs, h.userAssetLog(trust.BuyUid,
			trust.Order.Id,
			h.coinPair.CoinTo,
			trust.DealTotal.StringFixedBank(16),
			decimal.Zero.StringFixedBank(16),
			buyUserCoinTo.Balance,
			buyUserCoinTo.BalanceAvailable,
			buyUserCoinTo.BalanceLock,
			model.ASSET_TYPE_BUYOUT))

		// 卖家 coin to
		saleUserToBalanceAvailable, _ := decimal.NewFromString(saleUserCoinTo.BalanceAvailable)
		saleUserToBalanceLock, _ := decimal.NewFromString(saleUserCoinTo.BalanceLock)

		saleUserToBalance := saleUserToBalanceAvailable.Add(trust.SellerToOver).Sub(trust.SellerFee)

		saleUserCoinTo.Balance = saleUserToBalance.Add(saleUserToBalanceLock).StringFixedBank(16)
		saleUserCoinTo.BalanceAvailable = saleUserToBalance.StringFixedBank(16)

		results.UserAssetLogs = append(results.UserAssetLogs, h.userAssetLog(trust.SaleUid,
			trust.Order.Id, h.coinPair.CoinTo,
			trust.DealTotal.StringFixedBank(16),
			trust.SellerFee.StringFixedBank(16),
			saleUserCoinTo.Balance,
			saleUserCoinTo.BalanceAvailable,
			saleUserCoinTo.BalanceLock,
			model.ASSET_TYPE_SALEIN))

		// official buy fee
		if trust.BuyerFee.GreaterThan(decimal.Zero) && results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom] != nil &&
			trust.BuyUid != OFFICIAL_UID {
			if _, ok := results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom]; !ok {
				return false, fmt.Errorf("get official user account failed, key not exists, coin: %s", h.coinPair.CoinFrom)
			}

			officialBalance, _ := decimal.NewFromString(results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].Balance)
			officialAvailable, _ := decimal.NewFromString(results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].BalanceAvailable)
			results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].Balance = officialBalance.Add(trust.BuyerFee).StringFixedBank(16)
			results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].BalanceAvailable = officialAvailable.Add(trust.BuyerFee).StringFixedBank(16)

			results.UserAssetLogs = append(results.UserAssetLogs, h.userAssetLog(OFFICIAL_UID,
				trust.Order.Id,
				h.coinPair.CoinFrom,
				trust.BuyerFee.StringFixedBank(16),
				decimal.Zero.StringFixedBank(16),
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].Balance,
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].BalanceAvailable,
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom].BalanceLock,
				model.ASSET_TYPE_FEE))
		}

		if trust.SellerFee.GreaterThan(decimal.Zero) && results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo] != nil &&
			trust.SaleUid != OFFICIAL_UID {
			if _, ok := results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo]; !ok {
				return false, fmt.Errorf("get official user account failed, coin: %s", h.coinPair.CoinTo)
			}

			officialBalance, _ := decimal.NewFromString(results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].Balance)
			officialAvailable, _ := decimal.NewFromString(results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].BalanceAvailable)
			results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].Balance = officialBalance.Add(trust.SellerFee).StringFixedBank(16)
			results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].BalanceAvailable = officialAvailable.Add(trust.SellerFee).StringFixedBank(16)

			results.UserAssetLogs = append(results.UserAssetLogs, h.userAssetLog(OFFICIAL_UID,
				trust.Order.Id,
				h.coinPair.CoinTo,
				trust.SellerFee.StringFixedBank(16),
				decimal.Zero.StringFixedBank(16),
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].Balance,
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].BalanceAvailable,
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo].BalanceLock,
				model.ASSET_TYPE_FEE))
		}
	}

	return true, nil
}

func (h *HandlerDB) insertUserAssetLog(sess *xorm.Session, logs []*structs.UserAssetLog) (bool, error) {
	if len(logs) == 0 {
		return true, nil
	}
	return model.UserAssetLogModel.Inserts(sess, logs)
}

func (h *HandlerDB) insertOrderAndFinance(sess *xorm.Session, logs []*structs.OrderLogs) (bool, error) {
	for _, l := range logs {
		oid, err := model.OrderCoinModel.InsertItem(sess, l.Order)
		if err != nil {
			return false, err
		}
		h.logInfo("insert order success, oid: %d", oid)

		for _, f := range l.Finances {
			f.OrderCoinId = oid
		}

		_, err = model.OrderCoinFinanceModel.InsertItems(sess, l.Finances)
		if err != nil {
			return false, err
		}
		h.logInfo("insert finance success, length: %d", len(l.Finances))
	}
	return true, nil
}

// (active / passive) user coin
func (h *HandlerDB) updateUserAccount(sess *xorm.Session, results *structs.Results) (bool, error) {

	// 主动用户 被动用户 官方用户
	// 主动用户=被动用户，以主动用户为准
	var officialFeeAdded bool

	if results.ActiveTrust.Uid == OFFICIAL_UID {
		if fromFee, ok := results.TotalFeeNumber[h.coinPair.CoinFrom]; ok {
			results.ActiveAccount.FromOver = fromFee.Add(results.ActiveAccount.FromOver)
		}
		if toFee, ok := results.TotalFeeNumber[h.coinPair.CoinTo]; ok {
			results.ActiveAccount.ToOver = toFee.Add(results.ActiveAccount.ToOver)
		}
		officialFeeAdded = true
	}

	var err error
	if _, err = h.doUpdateOneUserAccount(sess, results.UserCoin, results.ActiveAccount,
		results.ActiveTrust.Uid, h.coinPair.CoinFrom, h.coinPair.CoinTo); err != nil {
		return false, err
	}

	// UID不会重复
	for uid, passiveAccount := range results.PassiveAccounts {
		if uid == results.ActiveTrust.Uid {
			continue
		}

		if uid == OFFICIAL_UID {
			if fromFee, ok := results.TotalFeeNumber[h.coinPair.CoinFrom]; ok {
				passiveAccount.FromOver = fromFee.Add(passiveAccount.FromOver)
			}
			if toFee, ok := results.TotalFeeNumber[h.coinPair.CoinTo]; ok {
				passiveAccount.ToOver = toFee.Add(passiveAccount.ToOver)
			}
			officialFeeAdded = true
		}

		if _, err := h.doUpdateOneUserAccount(sess, results.UserCoin, passiveAccount,
			uid, h.coinPair.CoinFrom, h.coinPair.CoinTo); err != nil {
			return false, err
		}
	}

	results.OfficialCoin.OriginalUserCoin = make(map[string]*structs.UserCoinBalance)
	results.OfficialCoin.NewUserCoin = make(map[string]*structs.UserCoinBalance)

	if !officialFeeAdded {
		for coin, number := range results.TotalFeeNumber {
			official, err := model.UserCoinModel.GetItemWithLock(sess, OFFICIAL_UID, coin)
			if err != nil {
				return false, fmt.Errorf("get item failed, coin: %s, err: %s", coin, err.Error())
			}

			balance, _ := decimal.NewFromString(official.Balance)
			available, _ := decimal.NewFromString(official.BalanceAvailable)
			lock, _ := decimal.NewFromString(official.BalanceLock)
			if ok, err := model.UserCoinModel.Update(sess, official.Id, coin, balance.Add(number).StringFixedBank(16),
				available.Add(number).StringFixedBank(16), lock.StringFixedBank(16)); !ok {
					h.logInfo("balance: %s, available: %s, lock: %s", balance.Add(number).StringFixedBank(16),
						available.Add(number).StringFixedBank(16), lock.StringFixedBank(16))
					return ok, fmt.Errorf("update official error, coin: %s, msg: %s", coin, err.Error())
			}

			results.OfficialCoin.OriginalUserCoin[coin] = copyUserCoinBalance(official)
			results.OfficialCoin.NewUserCoin[coin] = official
		}
	} else {
		if _, ok := results.UserCoin[OFFICIAL_UID]; ok {
			if coinFrom, ok := results.UserCoin[OFFICIAL_UID].OriginalUserCoin[h.coinPair.CoinFrom]; ok {
				results.OfficialCoin.OriginalUserCoin[h.coinPair.CoinFrom] = copyUserCoinBalance(coinFrom)
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinFrom] = coinFrom
			} else {
				return false, fmt.Errorf("get official user coin from user original coin failed")
			}

			if coinTo, ok := results.UserCoin[OFFICIAL_UID].OriginalUserCoin[h.coinPair.CoinTo]; ok {
				results.OfficialCoin.OriginalUserCoin[h.coinPair.CoinTo] = copyUserCoinBalance(coinTo)
				results.OfficialCoin.NewUserCoin[h.coinPair.CoinTo] = coinTo
			} else {
				return false, fmt.Errorf("get official user coin from user original coin failed")
			}
		}
	}

	return true, nil
}

// 修改官方用户资产
func (h *HandlerDB) updateOfficialUserCoin(session *xorm.Session, results *structs.Results) (bool, error) {

	results.OfficialCoin.OriginalUserCoin = make(map[string]*structs.UserCoinBalance)
	results.OfficialCoin.NewUserCoin = make(map[string]*structs.UserCoinBalance)

	for coin, number := range results.TotalFeeNumber {
		official, err := model.UserCoinModel.GetItemWithLock(session, OFFICIAL_UID, coin)
		if err != nil {
			return false, err
		}

		_, err = model.UserCoinModel.UpdateBalanceBuySQL(session, official.Id, number.StringFixedBank(16), coin)
		if err != nil {
			return false, err
		}

		results.OfficialCoin.OriginalUserCoin[coin] = copyUserCoinBalance(official)
		results.OfficialCoin.NewUserCoin[coin] = official
	}
	return true, nil
}

func (h *HandlerDB) doUpdateOneUserAccount(sess *xorm.Session, userAccounts map[int]*structs.OriginAndNewBalance,
	account *structs.Account, uid int, from, to string) (bool, error) {

	startTime := time.Now()
	min := decimal.NewFromFloat(0.0000000000001)
	if _, ok := userAccounts[uid]; !ok {
		userAccounts[uid] = &structs.OriginAndNewBalance{
			OriginalUserCoin: 		make(map[string]*structs.UserCoinBalance),
			NewUserCoin: 			make(map[string]*structs.UserCoinBalance),
		}
	}

	// 获取用户 coin from 资产数据，加排他查
	userFromAccount, err := model.UserCoinModel.GetItemWithLock(sess, uid, from)
	if err != nil {
		return false, fmt.Errorf("get user coin account failed, uid: %d, coin: %s, msg: %s", uid, from, err.Error())
	}

	h.logInfo("get user_coin_%s info, id: %d, uid: %d, balance: %v, available, %v, lock: %v", from,
		userFromAccount.Id, uid, userFromAccount.Balance, userFromAccount.BalanceAvailable, userFromAccount.BalanceLock)
	oldAvailable, _ := decimal.NewFromString(userFromAccount.BalanceAvailable)
	oldLock, _ := decimal.NewFromString(userFromAccount.BalanceLock)

	userAccounts[uid].OriginalUserCoin[from] = copyUserCoinBalance(userFromAccount)
	userAccounts[uid].NewUserCoin[from] = userFromAccount

	over := oldAvailable.Add(account.FromOver)
	if over.LessThan(decimal.Zero) {
		if over.Add(min).LessThan(decimal.Zero) {
			return false, fmt.Errorf("over account under zero, uid: %d, available: %v, account: %v",
				uid, oldAvailable, account.FromOver)
		} else {
			over = decimal.Zero
		}
	}

	lock := oldLock.Add(account.FromLock)
	if lock.LessThan(decimal.Zero) {
		if lock.Add(min).LessThan(decimal.Zero) {
			return false, fmt.Errorf("user lock balance below zero, uid: %d, coin: %s", uid, from)
		}
		lock = decimal.Zero
	}

	balance := over.Add(lock)
	_, err = model.UserCoinModel.Update(
		sess,
		userFromAccount.Id,
		from,
		formatDecimal2String(balance, 16),
		formatDecimal2String(over, 16),
		formatDecimal2String(lock, 16))

	if err != nil {
		h.logInfo("balance: %s, available: %s, lock: %s", formatDecimal2String(balance, 16),
			formatDecimal2String(over, 16), formatDecimal2String(lock, 16))
		return false, fmt.Errorf("update user coin, uid: %d, coin: %s, error: %s", uid, from, err.Error())
	}

	// 获取用户 coin to 用户资产，加排他查
	userToAccount, err := model.UserCoinModel.GetItemWithLock(sess, uid, to)
	if err != nil {
		return false, fmt.Errorf("user coin account has no item, uid: %d, coin: %s", uid, to)
	}

	h.logInfo("get user_coin_%s info, id: %d, uid: %d, balance: %v, available, %v, lock: %v", to,
		userToAccount.Id, uid, userToAccount.Balance, userToAccount.BalanceAvailable, userToAccount.BalanceLock)
	userAccounts[uid].OriginalUserCoin[to] = copyUserCoinBalance(userToAccount)
	userAccounts[uid].NewUserCoin[to] = userToAccount

	oldAvailable, _ = decimal.NewFromString(userToAccount.BalanceAvailable)
	over = oldAvailable.Add(account.ToOver)
	if over.LessThan(decimal.Zero) {
		if over.Add(min).LessThan(decimal.Zero) {
			return false, fmt.Errorf("over account under zero, uid: %d, available: %v, account: %v",
				uid, oldAvailable, account.ToOver)
		} else {
			over = decimal.Zero
		}
	}

	oldLock, _ = decimal.NewFromString(userToAccount.BalanceLock)
	lock = oldLock.Add(account.ToLock)
	if lock.LessThan(decimal.Zero) {
		if lock.Add(min).LessThan(decimal.Zero) {
			return false, fmt.Errorf("active user lock below zero, uid: %d, coin: %s", uid, to)
		}
		lock = decimal.Zero
	}

	balance = over.Add(lock)
	_, err = model.UserCoinModel.Update(
		sess,
		userToAccount.Id,
		to,
		formatDecimal2String(balance, 16),
		formatDecimal2String(over, 16),
		formatDecimal2String(lock, 16))

	if err != nil {
		h.logInfo("balance: %s, available: %s, lock: %s", formatDecimal2String(balance, 16), formatDecimal2String(over, 16), formatDecimal2String(lock, 16))
		return false, fmt.Errorf("update user coin error, uid: %d, coin: %s, msg: %s", uid, to, err.Error())
	}

	h.logInfo("user account, uid: %d, take time: %s", uid, time.Since(startTime).String())

	return true, nil
}

// 复制UserCoinBalance的值
func copyUserCoinBalance(balance *structs.UserCoinBalance) *structs.UserCoinBalance {
	userCoin := new(structs.UserCoinBalance)
	*userCoin = *balance
	return userCoin
}

func (h *HandlerDB) userAssetLog(uid int, orderId int, coin string,
	assetNum, feeNum, balance, available, lock, optType string) *structs.UserAssetLog {
	return &structs.UserAssetLog{
		Uid:              uid,
		Coin:             coin,
		OrderId:          orderId,
		AssetNumber:      assetNum,
		FeeNumber:        feeNum,
		Balance:          balance,
		BalanceAvailable: available,
		BalanceLock:      lock,
		Type:             optType,
		CreateTime:       time.Now(),
		UpdateTime:       time.Now(),
	}
}

// 更新主动用户和被动用户 TRUST COIN 表
func (h *HandlerDB) updateTrustCoins(sess *xorm.Session, results *structs.Results) (bool, error) {
	activeTrustCoin := results.ActiveTrust.TrustCoin
	if _, err := model.TrustCoinModel.Update(sess, activeTrustCoin.Id, activeTrustCoin); err != nil {
		h.logError("update active user trust info error, tid: %d, msg:%s", results.ActiveTrust.TrustId, err.Error())
		return false, err
	}

	h.logInfo("update active trust_coin success, tid: %d, uid: %d, deal: %v, status: %d",
		activeTrustCoin.Id, activeTrustCoin.Uid, activeTrustCoin.NumberDeal, activeTrustCoin.Status)

	for _, passive := range results.PassiveTrusts {
		passiveTrust := passive.TrustCoin
		if _, err := model.TrustCoinModel.Update(sess, passiveTrust.Id, passiveTrust); err != nil {
			h.logError("update passive trust_coin failed, error: %s", err.Error())
			return false, err
		}
		h.logInfo("update passive trust_coin success, tid: %d, uid: %d, deal: %v, status: %d",
			passiveTrust.Id, passiveTrust.Uid, passiveTrust.NumberDeal, passiveTrust.Status)
	}
	return true, nil
}

func (self HandlerDB) logInfo(format string, v ...interface{})  {
	log.Infof("["+self.coinPair.Name+"] " + format, v...)
}

func (self HandlerDB) logError(format string, v ...interface{})  {
	log.Errorf("["+self.coinPair.Name+"] " + format, v...)
}

func (self HandlerDB) logWarn(format string, v ...interface{})  {
	log.Warnf("["+self.coinPair.Name+"] " + format, v...)
}

